#include <iostream>
#include <fstream>
#include <sstream>
#include <stack>

using namespace std;

void errprintline (const char *msg, int line);

class CPU
{
	private:
		stringstream *str;
		string curInstr;
		string binInstr;
		
		typedef unsigned int INSTR;
		enum {
			ACC_OUT = 1, ACC_LATCH = 1 << 1,
			A_LATCH = 1 << 2, B_LATCH = 1 << 3,
			ALU_ADD = (1 << 4), ALU_AND = (1 << 5),
			ALU_OR = (1 << 6), ALU_NOT = (1 << 7),
			ALU_MUL = (1 << 8), ALU_DIV = (1 << 9),
			ONE_OUT = 1 << 10, ZERO_OUT = 1 << 11,
			PC_OUT = 1 << 11, PC_LATCH = 1 << 13,
			MAR_OUT = 1 << 14, MAR_LATCH = 1 << 15,
			MDR_OUT = 1 << 16, MDR_LATCH = 1 << 17,
			TEMP_OUT = 1 << 18, TEMP_LATCH = 1 << 19,
			IR_LATCH = 1 << 20, WRITE_READ = 1 << 21,
			DATA_ADDR = 1 << 22, BCOND_BL = 1 << 23, 
			BCOND_BG = 1 << 24, BCOND_BLE = 1 << 25, 
			BCOND_BGE = 1 << 26, BCOND_BE = 1 << 27, 
			BCOND_BA = 1 << 28, BCOND_JMP = 1 << 29, 
			BCOND_RET = 1 << 30, MAR_IMMED = 1 << 31
		};
		
	public:
		// Registers
		int A, B, ACC, PC, MAR, MDR, TEMP, IR, CC;

		Memory mem;
		const int ZERO, ONE;
		stack<int> funcRetAddr;
		vector<string> asm_instrs;

		/*
		 * instr refers to asm instructions
		 * op refers to micro operations like fetch, decode, etc.,
		 */
		int instrCount, // count of instructions executed
			opCount, // count of total operations executed till now
			opNum, // op num in current instr being exec currently
			instrTotal, // total num of instructions
			opTotal; // total ops in current instruction
		

		CPU (int setch);
		~CPU ();

		void errprint (const char *msg);
		
		int currentOperationNum ();
		string currentOperation ();
		string binaryOperation ();
		int getCurrentInstructionNumber ();
		int getCurrentOperationCount ();
		
		// Executes a given instructions file
		void Load (char* szFileName);
		int Fetch ();
		void Decode ();
		int ExecuteNextCycle ();
		int ExecuteNextInstruction ();
		void MicroProcessor (stringstream&);
		Memory* GetMemory ();
};
